<?php

class Controller_Site_Ajax extends Controller_Site
{
	const AJAX_HTML	= 0;
	const AJAX_JSON	= 1;
	const NORMAL	= 2;

	protected static $_catched = false;

	protected $ajaxDisableView = false;
	protected $ajaxReponceBody = '';
	protected $ajaxErrorMessages = array();
	protected $ajaxStatus = self::AJAX_STATUS_OK;

	public function setAjaxBody($body) {
		$this->ajaxReponceBody = $body;
		$this->_disableView();
	}
	
	public function addAjaxErrorMessage($message, $status = self::AJAX_STATUS_ERROR) {
		if($status == self::AJAX_STATUS_FATAL || $this->ajaxStatus == self::AJAX_STATUS_FATAL) {
			$this->ajaxStatus = self::AJAX_STATUS_FATAL;
			$this->ajaxErrorMessages = array( $message );
		}
		else {
			$this->ajaxStatus = $status;
			$this->ajaxErrorMessages[] = $message;
		}
	}

	public function setAjaxErrorMessages($messages, $status = self::AJAX_STATUS_ERROR) {
		$this->ajaxStatus = $status;
		$this->ajaxErrorMessages = $messages;
	}

	protected function _wrap($actionName) {
		try {
			$catched = self::$_catched;
			self::$_catched = TRUE;
			$this->getRequest()->setActionName($actionName);

			if ( !method_exists ($this, $actionName) ) {
				throw new Exception('Invalid action');
			}
			$this->preDispatchAjax();

			call_user_func(array($this, $actionName));

			$this->postDispatchAjax();
			
			if (!$this->ajaxDisableView) {
				$action = substr($actionName, 0, -6); // Remove the 'Action' suffix
				$viewPath = Zend_Controller_Action_HelperBroker::getExistingHelper('ViewRenderer')->getViewScript($action);
				$this->setAjaxBody($this->view->render($viewPath));
			}

		} catch (Exception $exc) {

			ob_end_clean(); // Важно! Иначе исключения, вызванные во view всё поломают
			
			try {
				Db::getConnection()->rollBack(); // Важно! Нужно откатить незакрытые транзакции
			}
			catch (PDOException $e) {
				
			}

			if ( !$this->getRequest()->isXmlHttpRequest() ) {
				throw $exc;
			}
			
			if ( ($exc instanceof Exception_Public) || (Debug::getInstance()->isDebugMode()) ) {

				if ($exc instanceof Exception_Validation) {
					$this->setAjaxErrorMessages($exc->getMessages());
				}
				elseif ($exc instanceof Exception_Redirect) {
					$this->_ajaxRedirect($exc->getLocation());
				}
				else {
					$this->addAjaxErrorMessage($exc->getMessage());
				}
				
			}
			else {
				$this->addAjaxErrorMessage(Settings::get('site', 'ajax', 'protected_error_message'), self::AJAX_STATUS_FATAL);
			}
		}
		
		if ($catched) {
			$this->getResponse()->setBody($this->ajaxReponceBody);
		}
		else {
			$this->_setAjaxResponse($this->ajaxReponceBody, $this->ajaxStatus, $this->ajaxErrorMessages);
		}
		
	}
	
	protected function _ajaxRedirect($url) {
		$this->ajaxStatus = 'REDIRECT';
		$this->ajaxReponceBody = $url;
	}

	protected function _redirect($url, array $options = array()) {
		if ($this->getRequest()->isXmlHttpRequest()) {
			throw new Exception_Redirect($url);
		}
		parent::_redirect($url, $options);
	}

	/**
	 * Set JSON data to response body
	 *
	 * @param array $data
	 * @return void
	 */
	protected function _setJson($data)
	{
		if($this->getRequest()->isXmlHttpRequest()) {
			$this->setAjaxBody($data);
		}
		else {
			parent::_setJson($data);
		}
	}

	protected function  _disableView() {
		$this->ajaxDisableView = TRUE;
		parent::_disableView();
	}

	/**
	 *  передача данных для ajax ответа
	 *
	 * @param array|string $body тело ответа
	 * @param string $status статус обработки
	 * @param array $errors массив ошибок выполнения
	 */
	protected function _setAjaxResponse($body, $status = self::AJAX_STATUS_OK, $messages = array()) {
		$result = array('status' => $status, 'body' => $body);

		if (!empty($messages)) {
			$result['messages'] = $messages;
		}
		parent::_setJson($result);
	}

	public function preDispatchAjax()
	{
		
	}

	public function postDispatchAjax()
	{

	}

	public function init()
	{
		$request = $this->getRequest();
		$wrap = (bool)$this->_getParam('ajax_wrap', TRUE);
		if ( $wrap && $request->isXmlHttpRequest() && !self::$_catched ) {
			$actionName = 'ajax'.$request->getActionName();
			$request->setActionName($actionName);
		}
		parent::init();
	}

	
	public function  __call($actionName, $args) {
		if (strpos($actionName, 'ajax') === 0) {
			$this->_wrap(substr($actionName, 4, strlen($actionName) - 4 ));
		} else {
			throw new Zend_Controller_Action_Exception(sprintf('Action "%s" does not exist and was not trapped in __call()', $actionName), 404);
		}
	}
}